﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;

namespace JN.Utility  
{
 
    public static class HSVConverter
    {
        private const int HSLMAX = 255;
        private const int RGBMAX = 255;
        private const int HSLUNDEFINED = (HSLMAX * 2 / 3);

        //public static void test()
        //{
        //    byte h = 0, s = 0, v = 0;
        //    RGBtoHSVGray(169, 199, 236, ref h, ref s, ref v);
        //}

        public static void RGBtoHSVGray(byte r, byte g, byte b, ref byte h, ref byte s, ref byte v)
        {
            #region 
            /*
            V = max(R,G,B);
            //  Saturation:
            temp = min(R,G,B);
            if V = 0 then // achromatics case
                S = 0//          H = 0
            else // chromatics case
                S = (V - temp)/V
            //  Hue:
            Cr = (V - R) / (V - temp)
            Cg = (V - G) / (V - temp)
            Cb = (V - B) / (V - temp)
            if R = V then H = Cb - Cg
            if G = V then H = 2 + Cr - Cb
            if B = V then H = 4 + Cg - Cr
            H = 60*H
            if H < 0 then H = H + 360
            */
            #endregion 

            double dH = 0;
            double dS = 0;
            double dV = 0; 
            dV = Math.Max(Math.Max(r, g), b); 
            double temp = Math.Min(Math.Min(r, g), b);  
            if ( dV == 0)
            {
                dS = 0;
                dH = 0;
            }
            else
            {
                dS = (dV - temp) / dV; 
            }

            if ((dV - temp) == 0)
            {
                dH = 0; 
            }
            else
            {
                double Cr = (dV - r) / (dV - temp);
                double Cg = (dV - g) / (dV - temp);
                double Cb = (dV - b) / (dV - temp);

                if (r == dV)
                {
                    dH = Cb - Cg;
                }
                if (g == dV)
                {
                    dH = 2 + Cr - Cb;
                }
                if (b == dV)
                {
                    dH = 4 + Cg - Cr;
                }
                dH = 60 * dH;
                if (dH < 0)
                {
                    dH = dH + 360;
                }
            }

            Debug.Assert(dH <= 360);
            Debug.Assert(dS >= 0 && dS <= 1);
            Debug.Assert(dV >= 0 && dV <= 255); 

            h = (byte)((dH / 360) * 255); 
            s = (byte)(dS * 255); 
            v = (byte)dV; 
            



 
            //byte cMax, cMin;				/* max and min RGB values */
            //double Rdelta, Gdelta, Bdelta;	/* intermediate value: % of spread from max*/

            //cMax = Math.Max(Math.Max(r, g), b);	/* calculate lightness */
            //cMin = Math.Min(Math.Min(r, g), b);
            //v = (byte)((((cMax + cMin) * HSLMAX) + RGBMAX) / (2 * RGBMAX));

            //if (cMax == cMin)
            //{			/* r=g=b --> achromatic case */
            //    s = 0;					/* saturation */
            //    h = HSLUNDEFINED;		/* hue */
            //}
            //else
            //{					/* chromatic case */
            //    if (v <= (HSLMAX / 2))	/* saturation */
            //        s = (byte)((((cMax - cMin) * HSLMAX) + ((cMax + cMin) / 2)) / (cMax + cMin));
            //    else
            //        s = (byte)((((cMax - cMin) * HSLMAX) + ((2 * RGBMAX - cMax - cMin) / 2)) / (2 * RGBMAX - cMax - cMin));
            //    /* hue */
            //    Rdelta = (double)((((cMax - r) * (HSLMAX / 6)) + ((cMax - cMin) / 2)) / (cMax - cMin));
            //    Gdelta = (double)((((cMax - g) * (HSLMAX / 6)) + ((cMax - cMin) / 2)) / (cMax - cMin));
            //    Bdelta = (double)((((cMax - b) * (HSLMAX / 6)) + ((cMax - cMin) / 2)) / (cMax - cMin));

            //    if (r == cMax)
            //        h = (byte)(Bdelta - Gdelta);
            //    else if (g == cMax)
            //        h = (byte)((HSLMAX / 3) + Rdelta - Bdelta);
            //    else /* B == cMax */
            //        h = (byte)(((2 * HSLMAX) / 3) + Gdelta - Rdelta);

            //    //		if (H < 0) H += HSLMAX;     //always false
            //    if (h > HSLMAX) h -= HSLMAX;
            //}
 
        }



        public static void RGBtoHSV(byte r, byte g, byte b, ref float h, ref float s, ref float v)
        {
            byte hGray = 0,   sGray = 0,   vGray = 0;
            RGBtoHSVGray( r,  g,  b, ref  hGray,  ref sGray, ref   vGray);
            h = (float)(hGray / 255.0 * 360);
            s = (float)(sGray / 255.0);
            v = (float)(vGray / 255.0); 
        }


        /// <summary>
        /// HSVtoRGB
        /// </summary>
        /// <param name="h">0-360</param>
        /// <param name="s">0-1</param>
        /// <param name="v">0-1</param>
        /// <param name="r"></param>
        /// <param name="g"></param>
        /// <param name="b"></param>
        public static void HSVtoRGB(float h, float s, float v,ref byte r,ref byte g,ref byte b)
        {
            /*
             if S = 0 then
               R = G = B = V
            else
                if H = 360 then
                    H = 0
                else
                    H = H/60
                 I = floor(H)
                 F = H - I;
                 M = V * ( 1 - S);
                 N = V * ( 1 - S * F);
                 K = V * ( 1 - S * (1 - F));
                 if(I == 0)then{ R = V;G = K;B = M;}
                 if(I == 1)then{ R = N;G = V;B = M;}
                 if(I == 2)then{ R = M;G = V;B = K;}
                 if(I == 3)then{ R = M;G = N;B = V;}
                 if(I == 4)then{ R = K;G = M;B = V;}
                 if(I == 5)then{ R = V;G = M;B = N;}
             */
 
            if (s == 0)
            {
                r = g = b = (byte)(v * 255);
            }
            else
            {
                if ( h == 360 )
                {
                    h = 0;
                }
                else 
                {
                    h = h / 60; 
                }
                float i = (float)Math.Floor(h);
                float f = h - i;
                float m = v * (1 - s);
                float n = v * (1 - s * f);
                float k = v * (1 - s * (1 - f));

                if (i == 0)
                {
                    r = (byte)(v * 255);
                    g = (byte)(k * 255);
                    b = (byte)(m * 255);
                }
                else if (i == 1)
                {
                    r = (byte)(n * 255);
                    g = (byte)(v * 255);
                    b = (byte)(m * 255);
                }
                else if (i == 2)
                { 
                    r = (byte)(m * 255);
                    g = (byte)(v * 255);
                    b = (byte)(k * 255);
                }
                else if (i == 3)
                {
                    r = (byte)(m * 255);
                    g = (byte)(n * 255);
                    b = (byte)(v * 255);
                }
                else if (i == 4)
                {
                    r = (byte)(k * 255);
                    g = (byte)(m * 255);
                    b = (byte)(v * 255);
                }
                else if (i == 5)
                {
                    r = (byte)(v * 255);
                    g = (byte)(m * 255);
                    b = (byte)(n * 255); 
                } 
            }



            //float m1, m2; 
            //if (v <= 0.5) m2 = v * (1 + s);
            //else m2 = v + s - v * s;

            //m1 = 2 * v - m2;

            //if (s == 0)
            //{
            //    r = g = b = (byte)(v * 255.0f);
            //}
            //else
            //{
            //    r = (byte)(HueToRGB(m1, m2, h + 120) * 255.0f);
            //    g = (byte)(HueToRGB(m1, m2, h) * 255.0f);
            //    b = (byte)(HueToRGB(m1, m2, h - 120) * 255.0f);
            //} 
        }

        private static float HueToRGB(float n1, float n2, float hue)
        { 
            float rValue; 
            if (hue > 360)
                hue = hue - 360;
            else if (hue < 0)
                hue = hue + 360;

            if (hue < 60)
                rValue = n1 + (n2 - n1) * hue / 60.0f;
            else if (hue < 180)
                rValue = n2;
            else if (hue < 240)
                rValue = n1 + (n2 - n1) * (240 - hue) / 60;
            else
                rValue = n1; 
            return rValue;
        }


    }

}
